package service

import (
	"context"
	"sync"
	"time"
	"todo-list/repository/db/dao"
	"todo-list/repository/db/model"
	"todo-list/types"
)

var (
	TaskSrvIns  *TaskSrv
	TaskSrvOnce sync.Once
)

type TaskSrv struct {
}

func (t *TaskSrv) ShowTaskInfo(ctx context.Context, req *types.ShowTaskReq) (*types.TaskRespDetail, error) {
	taskDao := dao.NewTaskDao(ctx)
	task, err := taskDao.FindById(req.ID)
	if err != nil {
		return nil, err
	}
	return types.Model2TaskDetail(task), err
}

func (t *TaskSrv) ViewPublicTask(ctx context.Context, req *types.ListTaskReq) (*types.PagedData, error) {
	taskDao := dao.NewTaskDao(ctx)
	optional := &dao.OptionalParam{}
	optional.SetPage(req.GetPage())
	optional.SetLimit(req.GetLimit())
	optional.IgnoreContent = !req.NeedContent
	param := NewParam(req)
	tasks, rows, err := taskDao.FindTask(param, optional)
	return &types.PagedData{
		Item:  types.Models2Tasks(tasks),
		Total: rows,
	}, err
}

func (t *TaskSrv) ViewSelfTask(ctx context.Context, userId uint, req *types.ListTaskReq) (*types.PagedData, error) {
	taskDao := dao.NewTaskDao(ctx)
	optional := &dao.OptionalParam{}
	optional.SetPage(req.GetPage())
	optional.SetLimit(req.GetLimit())
	optional.SetId(userId)
	optional.IgnoreContent = !req.NeedContent
	param := NewParam(req)
	tasks, rows, err := taskDao.FindTask(param, optional)
	return &types.PagedData{
		Item:  types.Models2Tasks(tasks),
		Total: rows,
	}, err
}

func (t *TaskSrv) CreateTask(ctx context.Context, userId uint, req *types.CreateTaskReq) (err error) {
	taskDao := dao.NewTaskDao(ctx)
	var status model.TaskStatus
	if req.Status == nil || !(*req.Status).Validate() {
		status = model.NotStart
	} else {
		status = *req.Status
	}
	task := model.NewTask(userId, req.Title, req.Content, status, req.Public)
	err = taskDao.CreateTask(task)
	return
}
func (t *TaskSrv) UpdateTask(ctx context.Context, userId uint, req *types.UpdateTaskReq) (err error) {
	taskDao := dao.NewTaskDao(ctx)
	task, err := taskDao.FindById(req.ID)
	if err != nil {
		return
	}
	if req.Title != "" {
		task.Title = req.Title
	}
	task.Content = req.Content
	if req.Status != 0 {
		updateStatus(req.Status, &task)
	}
	if req.Public != nil {
		task.Public = *req.Public
	}
	err = taskDao.UpdateTask(&task, userId)
	return
}

func (t *TaskSrv) UpdateStatusOrPublicBatch(ctx context.Context, userId uint, req *types.UpdateTaskBatchReq) (err error) {
	taskDao := dao.NewTaskDao(ctx)
	tasks, err := taskDao.FindByIds(req.IDs)
	taskDao.UseTransaction()
	for _, t := range tasks {
		if req.Status != nil {
			updateStatus(*req.Status, &t)
		}
		if req.Public != nil {
			t.Public = *req.Public
		}
		err = taskDao.UpdateTask(&t, userId)
		if err != nil {
			taskDao.Rollback()
			return
		}
	}
	taskDao.Commit()
	return
}

func updateStatus(status model.TaskStatus, task *model.Task) {
	now := time.Now()
	switch status {
	case model.NotStart:
		task.StartTime = nil
		task.EndTime = nil
		task.AbandonTime = nil
	case model.Doing:
		task.StartTime = &now
		task.EndTime = nil
		task.AbandonTime = nil
	case model.Done:
		if task.StartTime == nil {
			task.StartTime = &task.CreatedAt
		}
		task.EndTime = &now
		task.AbandonTime = nil
	case model.Abandon:
		task.EndTime = nil
		task.AbandonTime = &now
	default:
		return
	}
	task.Status = status
}

func GetTaskSrv() *TaskSrv {
	TaskSrvOnce.Do(func() {
		TaskSrvIns = &TaskSrv{}
	})
	return TaskSrvIns
}

func NewParam(req *types.ListTaskReq) model.ModelMap {
	param := make(model.ModelMap)
	if req.Status != nil {
		param.Set("status", *req.Status)
	}
	if req.Text != nil {
		param.Set("text", *req.Text)
	}
	if req.CreateAt != nil {
		param.Set("createAt", *req.CreateAt)
	}
	if req.PublicOnly {
		param.Set("public", true)
	}
	return param
}
